{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "b0532eb7",
   "metadata": {},
   "source": [
    "### Check Dependencies"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "9ec40f4d",
   "metadata": {},
   "outputs": [],
   "source": [
    "#!pip3 install --upgrade --quiet langchain langchain-community langchain-openai langchain_mistralai neo4j==5.19.0 tiktoken tokenizers"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "4108ab2a",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Name: langchain\n",
      "Version: 0.1.17\n",
      "Summary: Building applications with LLMs through composability\n",
      "Home-page: https://github.com/langchain-ai/langchain\n",
      "Author: \n",
      "Author-email: \n",
      "License: MIT\n",
      "Location: /Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages\n",
      "Requires: aiohttp, dataclasses-json, jsonpatch, langchain-community, langchain-core, langchain-text-splitters, langsmith, numpy, pydantic, PyYAML, requests, SQLAlchemy, tenacity\n",
      "Required-by: \n",
      "---\n",
      "Name: neo4j\n",
      "Version: 5.19.0\n",
      "Summary: Neo4j Bolt driver for Python\n",
      "Home-page: \n",
      "Author: \n",
      "Author-email: \"Neo4j, Inc.\" <drivers@neo4j.com>\n",
      "License: Apache License, Version 2.0\n",
      "Location: /Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages\n",
      "Requires: pytz\n",
      "Required-by: \n",
      "---\n",
      "Name: langchain-mistralai\n",
      "Version: 0.1.5\n",
      "Summary: An integration package connecting Mistral and LangChain\n",
      "Home-page: https://github.com/langchain-ai/langchain\n",
      "Author: \n",
      "Author-email: \n",
      "License: MIT\n",
      "Location: /Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages\n",
      "Requires: httpx, httpx-sse, langchain-core, tokenizers\n",
      "Required-by: \n"
     ]
    }
   ],
   "source": [
    "!pip3 show langchain neo4j langchain_mistralai"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "57fe23fb",
   "metadata": {},
   "source": [
    "### Load environment variables"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "a1218f28",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "NEO4J_URI = bolt://localhost:7687\n",
      "NEO4J_USERNAME = neo4j\n",
      "NEO4J_PASSWORD = neo4j123\n"
     ]
    }
   ],
   "source": [
    "from dotenv import load_dotenv\n",
    "import os\n",
    "load_dotenv()\n",
    "\n",
    "print(\"NEO4J_URI = \" + os.getenv(\"NEO4J_URI\"))\n",
    "print(\"NEO4J_USERNAME = \" + os.getenv(\"NEO4J_USERNAME\"))\n",
    "print(\"NEO4J_PASSWORD = \" + os.getenv(\"NEO4J_PASSWORD\"))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0f4595a8",
   "metadata": {},
   "source": [
    "### Create Neo4jGraph\n",
    "\n",
    "Make sure the Neo4j instance is running and the environment variables are set correctly."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "b87033b3",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_community.graphs import Neo4jGraph\n",
    "graph = Neo4jGraph()\n",
    "# graph = Neo4jGraph(url=\"bolt://localhost:7687\", username=\"neo4j\", password=\"neo4j123\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ee99103f",
   "metadata": {},
   "source": [
    "### Check graph schema"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "39f09c26",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Node properties:\\nPerson {tags: STRING, name: STRING, description: STRING}\\nSoftwareSystem {tags: STRING, name: STRING, description: STRING}\\nContainer {tags: STRING, name: STRING, description: STRING}\\nComponent {tags: STRING, name: STRING, description: STRING}\\nElement {name: STRING, embedding: LIST, metadata.type: STRING, metadata.source: STRING, metadata.name: STRING, metadata.parent: STRING, metadata.tags: STRING, text: STRING}\\nRelationship properties:\\nUses {description: STRING}\\nThe relationships:\\n(:Person)-[:Uses]->(:Container)\\n(:Person)-[:Uses]->(:SoftwareSystem)\\n(:Person)-[:Uses]->(:Person)\\n(:SoftwareSystem)-[:Uses]->(:Person)\\n(:SoftwareSystem)-[:Uses]->(:SoftwareSystem)\\n(:SoftwareSystem)-[:Contains]->(:Container)\\n(:Container)-[:Uses]->(:Component)\\n(:Container)-[:Uses]->(:Container)\\n(:Container)-[:Uses]->(:SoftwareSystem)\\n(:Container)-[:Contains]->(:Component)\\n(:Component)-[:Uses]->(:Component)\\n(:Component)-[:Uses]->(:Container)\\n(:Component)-[:Uses]->(:SoftwareSystem)'"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "graph.schema"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "287ca85b",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'node_props': {'Person': [{'property': 'tags', 'type': 'STRING'},\n",
       "   {'property': 'name', 'type': 'STRING'},\n",
       "   {'property': 'description', 'type': 'STRING'}],\n",
       "  'SoftwareSystem': [{'property': 'tags', 'type': 'STRING'},\n",
       "   {'property': 'name', 'type': 'STRING'},\n",
       "   {'property': 'description', 'type': 'STRING'}],\n",
       "  'Container': [{'property': 'tags', 'type': 'STRING'},\n",
       "   {'property': 'name', 'type': 'STRING'},\n",
       "   {'property': 'description', 'type': 'STRING'}],\n",
       "  'Component': [{'property': 'tags', 'type': 'STRING'},\n",
       "   {'property': 'name', 'type': 'STRING'},\n",
       "   {'property': 'description', 'type': 'STRING'}],\n",
       "  'Element': [{'property': 'name', 'type': 'STRING'},\n",
       "   {'property': 'embedding', 'type': 'LIST'},\n",
       "   {'property': 'metadata.type', 'type': 'STRING'},\n",
       "   {'property': 'metadata.source', 'type': 'STRING'},\n",
       "   {'property': 'metadata.name', 'type': 'STRING'},\n",
       "   {'property': 'metadata.parent', 'type': 'STRING'},\n",
       "   {'property': 'metadata.tags', 'type': 'STRING'},\n",
       "   {'property': 'text', 'type': 'STRING'}]},\n",
       " 'rel_props': {'Uses': [{'property': 'description', 'type': 'STRING'}]},\n",
       " 'relationships': [{'start': 'Person', 'type': 'Uses', 'end': 'Container'},\n",
       "  {'start': 'Person', 'type': 'Uses', 'end': 'SoftwareSystem'},\n",
       "  {'start': 'Person', 'type': 'Uses', 'end': 'Person'},\n",
       "  {'start': 'SoftwareSystem', 'type': 'Uses', 'end': 'Person'},\n",
       "  {'start': 'SoftwareSystem', 'type': 'Uses', 'end': 'SoftwareSystem'},\n",
       "  {'start': 'SoftwareSystem', 'type': 'Contains', 'end': 'Container'},\n",
       "  {'start': 'Container', 'type': 'Uses', 'end': 'Component'},\n",
       "  {'start': 'Container', 'type': 'Uses', 'end': 'Container'},\n",
       "  {'start': 'Container', 'type': 'Uses', 'end': 'SoftwareSystem'},\n",
       "  {'start': 'Container', 'type': 'Contains', 'end': 'Component'},\n",
       "  {'start': 'Component', 'type': 'Uses', 'end': 'Component'},\n",
       "  {'start': 'Component', 'type': 'Uses', 'end': 'Container'},\n",
       "  {'start': 'Component', 'type': 'Uses', 'end': 'SoftwareSystem'}],\n",
       " 'metadata': {'constraint': [],\n",
       "  'index': [{'id': 3,\n",
       "    'name': 'SoftwareSystem_index',\n",
       "    'state': 'ONLINE',\n",
       "    'populationPercent': 100.0,\n",
       "    'type': 'VECTOR',\n",
       "    'entityType': 'NODE',\n",
       "    'labelsOrTypes': ['SoftwareSystem'],\n",
       "    'properties': ['embedding'],\n",
       "    'indexProvider': 'vector-2.0',\n",
       "    'owningConstraint': None,\n",
       "    'lastRead': neo4j.time.DateTime(2024, 5, 2, 13, 32, 27, 222000000, tzinfo=<UTC>),\n",
       "    'readCount': 27,\n",
       "    'trackedSince': neo4j.time.DateTime(2024, 5, 2, 0, 57, 15, 192000000, tzinfo=<UTC>),\n",
       "    'options': {'indexProvider': 'vector-2.0',\n",
       "     'indexConfig': {'vector.dimensions': 1024,\n",
       "      'vector.similarity_function': 'cosine'}},\n",
       "    'failureMessage': '',\n",
       "    'createStatement': \"CREATE VECTOR INDEX `SoftwareSystem_index` FOR (n:`SoftwareSystem`) ON (n.`embedding`) OPTIONS {indexConfig: {`vector.dimensions`: 1024,`vector.similarity_function`: 'cosine'}, indexProvider: 'vector-2.0'}\"},\n",
       "   {'id': 1,\n",
       "    'name': 'index_343aff4e',\n",
       "    'state': 'ONLINE',\n",
       "    'populationPercent': 100.0,\n",
       "    'type': 'LOOKUP',\n",
       "    'entityType': 'NODE',\n",
       "    'labelsOrTypes': None,\n",
       "    'properties': None,\n",
       "    'indexProvider': 'token-lookup-1.0',\n",
       "    'owningConstraint': None,\n",
       "    'lastRead': neo4j.time.DateTime(2024, 5, 2, 14, 58, 51, 978000000, tzinfo=<UTC>),\n",
       "    'readCount': 967,\n",
       "    'trackedSince': neo4j.time.DateTime(2024, 5, 2, 0, 7, 21, 477000000, tzinfo=<UTC>),\n",
       "    'options': {'indexProvider': 'token-lookup-1.0', 'indexConfig': {}},\n",
       "    'failureMessage': '',\n",
       "    'createStatement': 'CREATE LOOKUP INDEX `index_343aff4e` FOR (n) ON EACH labels(n)'},\n",
       "   {'id': 2,\n",
       "    'name': 'index_f7700477',\n",
       "    'state': 'ONLINE',\n",
       "    'populationPercent': 100.0,\n",
       "    'type': 'LOOKUP',\n",
       "    'entityType': 'RELATIONSHIP',\n",
       "    'labelsOrTypes': None,\n",
       "    'properties': None,\n",
       "    'indexProvider': 'token-lookup-1.0',\n",
       "    'owningConstraint': None,\n",
       "    'lastRead': neo4j.time.DateTime(2024, 5, 2, 7, 56, 35, 397000000, tzinfo=<UTC>),\n",
       "    'readCount': 4,\n",
       "    'trackedSince': neo4j.time.DateTime(2024, 5, 2, 0, 7, 21, 484000000, tzinfo=<UTC>),\n",
       "    'options': {'indexProvider': 'token-lookup-1.0', 'indexConfig': {}},\n",
       "    'failureMessage': '',\n",
       "    'createStatement': 'CREATE LOOKUP INDEX `index_f7700477` FOR ()-[r]-() ON EACH type(r)'}]}}"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "graph.structured_schema"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b130e26b",
   "metadata": {},
   "source": [
    "### Create Cypher Query"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "399addd7",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[{'n': {'name': 'Personal Banking Customer',\n",
       "   'description': 'A customer of the bank, with personal bank accounts.',\n",
       "   'tags': 'Element,Person,Customer'}},\n",
       " {'n': {'name': 'Customer Service Staff',\n",
       "   'description': 'Customer service staff within the bank.',\n",
       "   'tags': 'Element,Person,Bank Staff'}},\n",
       " {'n': {'name': 'Back Office Staff',\n",
       "   'description': 'Administration and support staff within the bank.',\n",
       "   'tags': 'Element,Person,Bank Staff'}}]"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "## What are the persons using systems?\n",
    "graph.query(\"MATCH (n:Person) RETURN n\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "44c21544",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[{'ss.name': 'ATM'}, {'ss.name': 'Internet Banking System'}]"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "## What software systems are used by customers?\n",
    "graph.query(\"\"\"\n",
    "    MATCH (p:Person)-[:Uses]->(ss:SoftwareSystem)\n",
    "    WHERE p.tags CONTAINS \"Customer\"\n",
    "    RETURN DISTINCT ss.name\n",
    "    \"\"\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "df9a2b88",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[{'ss.name': 'ATM'}]"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "## What software systems are used by customers to withdraw cash?\n",
    "graph.query(\"\"\"\n",
    "    MATCH (p:Person)-[:Uses]->(ss:SoftwareSystem)\n",
    "    WHERE p.tags CONTAINS \"Customer\" and ss.description CONTAINS \"withdraw cash\"\n",
    "    RETURN DISTINCT ss.name\n",
    "    \"\"\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bc747d13",
   "metadata": {},
   "source": [
    "### Create LLM"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "dd6c9c3b",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "MISTRAL_MODEL = open-mistral-7b\n"
     ]
    }
   ],
   "source": [
    "# from langchain_openai import ChatOpenAI\n",
    "# os.environ[\"OPENAI_API_KEY\"] = .......\n",
    "# llm = ChatOpenAI(model=\"gpt-3.5-turbo\", temperature=0)\n",
    "\n",
    "from langchain_mistralai.chat_models import ChatMistralAI\n",
    "llm = ChatMistralAI(mistral_api_key=os.getenv(\"MISTRAL_API_KEY\"), model=os.getenv(\"MISTRAL_MODEL\"), temperature=0)\n",
    "\n",
    "print(\"MISTRAL_MODEL = \" + os.getenv(\"MISTRAL_MODEL\"))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0900e165",
   "metadata": {},
   "source": [
    "### Create GraphCypherQAChain\n",
    "\n",
    "Please refer to API documentation for more details.\n",
    "\n",
    "https://api.python.langchain.com/en/latest/chains/langchain.chains.graph_qa.cypher.GraphCypherQAChain.html"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "ddf43ecf",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain.chains import GraphCypherQAChain\n",
    "#  input_key: str = \"query\"  \n",
    "#  output_key: str = \"result\" \n",
    "\n",
    "chain = GraphCypherQAChain.from_llm(graph=graph, llm=llm, verbose=True)\n",
    "# top_k=2\n",
    "# return_intermediate_steps=True\n",
    "# return_direct=True\n",
    "\n",
    "\n",
    "# class GraphCypherQAChain(Chain):\n",
    "#     graph: GraphStore = Field(exclude=True)\n",
    "#     cypher_generation_chain: LLMChain\n",
    "#     qa_chain: LLMChain\n",
    "#     graph_schema: str\n",
    "#     input_key: str = \"query\"  #: :meta private:\n",
    "#     output_key: str = \"result\"  #: :meta private:\n",
    "#     top_k: int = 10\n",
    "#     \"\"\"Number of results to return from the query\"\"\"\n",
    "#     return_intermediate_steps: bool = False\n",
    "#     \"\"\"Whether or not to return the intermediate steps along with the final answer.\"\"\"\n",
    "#     return_direct: bool = False\n",
    "#     \"\"\"Whether or not to return the result of querying the graph directly.\"\"\"\n",
    "#     cypher_query_corrector: Optional[CypherQueryCorrector] = None\n",
    "#     \"\"\"Optional cypher validation tool\"\"\"\n",
    "\n",
    "# from_llm(\n",
    "#         cls,\n",
    "#         llm: Optional[BaseLanguageModel] = None,\n",
    "#         *,\n",
    "#         qa_prompt: Optional[BasePromptTemplate] = None,\n",
    "#         cypher_prompt: Optional[BasePromptTemplate] = None,\n",
    "#         cypher_llm: Optional[BaseLanguageModel] = None,\n",
    "#         qa_llm: Optional[BaseLanguageModel] = None,\n",
    "#         exclude_types: List[str] = [],\n",
    "#         include_types: List[str] = [],\n",
    "#         validate_cypher: bool = False,\n",
    "#         qa_llm_kwargs: Optional[Dict[str, Any]] = None,\n",
    "#         cypher_llm_kwargs: Optional[Dict[str, Any]] = None,\n",
    "#         **kwargs: Any,\n",
    "#     ) -> GraphCypherQAChain:"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "00c36309",
   "metadata": {},
   "source": [
    "### Check the default prompts\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "588ab77d",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CYPHER_GENERATION_PROMPT = \n",
      "input_variables=['question', 'schema'] template='Task:Generate Cypher statement to query a graph database.\\nInstructions:\\nUse only the provided relationship types and properties in the schema.\\nDo not use any other relationship types or properties that are not provided.\\nSchema:\\n{schema}\\nNote: Do not include any explanations or apologies in your responses.\\nDo not respond to any questions that might ask anything else than for you to construct a Cypher statement.\\nDo not include any text except the generated Cypher statement.\\n\\nThe question is:\\n{question}'\n",
      "CYPHER_QA_PROMPT = \n",
      "input_variables=['context', 'question'] template=\"You are an assistant that helps to form nice and human understandable answers.\\nThe information part contains the provided information that you must use to construct an answer.\\nThe provided information is authoritative, you must never doubt it or try to use your internal knowledge to correct it.\\nMake the answer sound as a response to the question. Do not mention that you based the result on the given information.\\nHere is an example:\\n\\nQuestion: Which managers own Neo4j stocks?\\nContext:[manager:CTL LLC, manager:JANE STREET GROUP LLC]\\nHelpful Answer: CTL LLC, JANE STREET GROUP LLC owns Neo4j stocks.\\n\\nFollow this example when generating answers.\\nIf the provided information is empty, say that you don't know the answer.\\nInformation:\\n{context}\\n\\nQuestion: {question}\\nHelpful Answer:\"\n"
     ]
    }
   ],
   "source": [
    "from langchain.chains.graph_qa.prompts import CYPHER_GENERATION_PROMPT, CYPHER_QA_PROMPT\n",
    "\n",
    "print(\"CYPHER_GENERATION_PROMPT = \", flush=True)\n",
    "print(CYPHER_GENERATION_PROMPT)\n",
    "\n",
    "print(\"CYPHER_QA_PROMPT = \", flush=True)\n",
    "print(CYPHER_QA_PROMPT)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2576bf47",
   "metadata": {},
   "source": [
    "### Ask question to QA chain\n",
    "\n",
    "The **question** must be passed as a dictionary with a key \"query\" and the value is the question.\n",
    "\n",
    "The QA chain will generate a cypher query statement using `CYPHER_GENERATION_PROMPT` and return the answer to the question.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "d9b3782f",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001b[1m> Entering new GraphCypherQAChain chain...\u001b[0m\n",
      "Generated Cypher:\n",
      "\u001b[32;1m\u001b[1;3mcypher\n",
      "MATCH (p:Person {name: \"Back Office Staff\"})-[:Uses]->(ss:SoftwareSystem)\n",
      "RETURN ss.name\n",
      "\u001b[0m\n",
      "Full Context:\n",
      "\u001b[32;1m\u001b[1;3m[{'ss.name': 'Mainframe Banking System'}]\u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'query': 'What software systems are used by Back Office Staff?',\n",
       " 'result': 'The Back Office Staff uses the Mainframe Banking System.'}"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "response = chain.invoke({\"query\": \"What software systems are used by Back Office Staff?\"})\n",
    "response"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "f46f7b1f",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001b[1m> Entering new GraphCypherQAChain chain...\u001b[0m\n",
      "Generated Cypher:\n",
      "\u001b[32;1m\u001b[1;3mcypher\n",
      "MATCH (p:Person {name: \"Personal Banking Customer\"})-[:Uses]->(ss:SoftwareSystem)\n",
      "RETURN ss.name\n",
      "\u001b[0m\n",
      "Full Context:\n",
      "\u001b[32;1m\u001b[1;3m[{'ss.name': 'ATM'}, {'ss.name': 'Internet Banking System'}]\u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'query': 'What software systems are used by Personal Banking Customer?',\n",
       " 'result': 'Personal Banking Customers use ATM and Internet Banking System software systems.'}"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "response = chain.invoke({\"query\": \"What software systems are used by Personal Banking Customer?\"})\n",
    "response"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "036634a9",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001b[1m> Entering new GraphCypherQAChain chain...\u001b[0m\n",
      "Generated Cypher:\n",
      "\u001b[32;1m\u001b[1;3mcypher\n",
      "MATCH (p:Person)-[:Uses]->(ss:SoftwareSystem)\n",
      "RETURN ss.name\n",
      "\u001b[0m\n",
      "Full Context:\n",
      "\u001b[32;1m\u001b[1;3m[{'ss.name': 'ATM'}, {'ss.name': 'Internet Banking System'}, {'ss.name': 'Mainframe Banking System'}, {'ss.name': 'Mainframe Banking System'}]\u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'query': 'What software systems are used by customers?',\n",
       " 'result': 'Customers use ATM, Internet Banking System, and Mainframe Banking System software systems.'}"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "response = chain.invoke({\"query\": \"What software systems are used by customers?\"})\n",
    "response"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5fb35960",
   "metadata": {},
   "source": [
    "### Promt Refinement\n",
    "\n",
    "https://python.langchain.com/docs/integrations/graphs/memgraph/#prompt-refinement\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "74c37a49",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_core.prompts import PromptTemplate\n",
    "\n",
    "# ## Refine the CYPHER_GENERATION_TEMPLATE by adding examples\n",
    "\n",
    "MY_CYPHER_GENERATION_TEMPLATE = \"\"\"\n",
    "Task: Generate a syntactically correct cypher statement to query a graph database.\n",
    "Instructions:\n",
    "Use only the provided relationship types and properties in the schema.\n",
    "Do not use any other relationship types or properties that are not provided.\n",
    "\n",
    "Schema:\n",
    "{schema}\n",
    "\n",
    "Note: Do not include any explanations or apologies in your responses.\n",
    "Do not respond to any questions that might ask anything else than for you to construct a Cypher statement.\n",
    "Do not include any text except the generated Cypher statement.\n",
    "\n",
    "Here are a few examples of generated Cypher statements for particular questions:\n",
    "# What software systems are used by Bank Staff?\n",
    "MATCH (p:Person)-[:Uses]->(ss:SoftwareSystem)\n",
    "WHERE p.tags CONTAINS \"Bank Staff\"\n",
    "RETURN DISTINCT ss.name\n",
    "# What software systems are used by customers to withdraw cash?\n",
    "MATCH (p:Person)-[:Uses]->(ss:SoftwareSystem)\n",
    "WHERE p.tags CONTAINS \"Customer\" and ss.description CONTAINS \"withdraw cash\"\n",
    "RETURN DISTINCT ss.name\n",
    "    \n",
    "The question is:\n",
    "{question}\n",
    "\"\"\"\n",
    "\n",
    "MY_CYPHER_GENERATION_PROMPT = PromptTemplate(\n",
    "    input_variables=[\"schema\", \"question\"], template=MY_CYPHER_GENERATION_TEMPLATE\n",
    ")\n",
    "\n",
    "## Use the default CYPHER_GENERATION_PROMPT\n",
    "# MY_CYPHER_GENERATION_PROMPT = CYPHER_GENERATION_PROMPT\n",
    "\n",
    "## Use the default CYPHER_QA_PROMPT\n",
    "# MY_CYPHER_QA_PROMPT = CYPHER_QA_PROMPT\n",
    "MY_CYPHER_QA_TEMPLATE = \"\"\"\n",
    "You are an assistant that helps to form nice and human understandable answers.\n",
    "The context part contains the provided information that you must use to construct an answer.\n",
    "The provided information is authoritative, you must never doubt it or try to use your internal knowledge to correct it.\n",
    "Make the answer sound as a response to the question. \n",
    "Do not mention that you based the result on the given context.\n",
    "Do not make up anything which does not exist in the provided context.\n",
    "\n",
    "Here is an example:\n",
    "Question: What software systems are used by customers?\n",
    "Context: ATM, Internet Banking System\n",
    "Helpful Answer: ATM and Internet Banking System.\n",
    "\n",
    "Follow this example when generating answers. If the provided context is empty, say that you don't know the answer.\n",
    "\n",
    "Context: {context}\n",
    "\n",
    "Question: {question}\n",
    "Helpful Answer:\n",
    "\"\"\"\n",
    "\n",
    "MY_CYPHER_QA_PROMPT = PromptTemplate(\n",
    "    input_variables=['context', 'question'], template=MY_CYPHER_QA_TEMPLATE\n",
    ")\n",
    "\n",
    "chain = GraphCypherQAChain.from_llm(llm=llm, graph=graph, qa_prompt=MY_CYPHER_QA_PROMPT,cypher_prompt=MY_CYPHER_GENERATION_PROMPT,verbose=True)\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "68929886",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001b[1m> Entering new GraphCypherQAChain chain...\u001b[0m\n",
      "Generated Cypher:\n",
      "\u001b[32;1m\u001b[1;3mMATCH (p:Person)-[:Uses]->(ss:SoftwareSystem)\n",
      "WHERE p.tags CONTAINS \"Customer\"\n",
      "RETURN DISTINCT ss.name\u001b[0m\n",
      "Full Context:\n",
      "\u001b[32;1m\u001b[1;3m[{'ss.name': 'ATM'}, {'ss.name': 'Internet Banking System'}]\u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'query': 'What software systems are used by customers?',\n",
       " 'result': 'Customers use ATM, Internet Banking System, and Mainframe Banking System software systems.'}"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "responseresponse = chain.invoke({\"query\": \"What software systems are used by customers?\"})\n",
    "response"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "e9bc8929",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001b[1m> Entering new GraphCypherQAChain chain...\u001b[0m\n",
      "Generated Cypher:\n",
      "\u001b[32;1m\u001b[1;3mMATCH (p:Person)-[:Uses]->(ss:SoftwareSystem)\n",
      "WHERE p.tags CONTAINS \"Customer\" and ss.description CONTAINS \"withdraw cash\"\n",
      "RETURN DISTINCT ss.name\u001b[0m\n",
      "Full Context:\n",
      "\u001b[32;1m\u001b[1;3m[{'ss.name': 'ATM'}]\u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'query': 'What software systems are used by customers?',\n",
       " 'result': 'Customers use ATM, Internet Banking System, and Mainframe Banking System software systems.'}"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "responseresponse = chain.invoke({\"query\": \"What systems can be used by customers to withdraw cash?\"})\n",
    "response"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "214f2f09",
   "metadata": {},
   "source": [
    "### Few-shot examples\n",
    "    "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "abb2c24f",
   "metadata": {},
   "outputs": [],
   "source": [
    "examples = [\n",
    "    {\n",
    "        \"question\": \"who are the users\",\n",
    "        \"query\": \"MATCH (p:Person) RETURN distinct p\",\n",
    "    },\n",
    "    {\n",
    "        \"question\": \"what software systems are used by customers\",\n",
    "        \"query\": \"MATCH (p:Person)-[:Uses]->(s:SoftwareSystem) WHERE p.tags contains 'Customer' RETURN DISTINCT s.name,s.description\",\n",
    "    },\n",
    "    {\n",
    "        \"question\": \"what software systems are used by customers to withdraw cash\",\n",
    "        \"query\": \"MATCH (p:Person)-[:Uses]->(s:SoftwareSystem) WHERE p.tags contains 'Customer' AND s.description CONTAINS 'withdraw cash' RETURN DISTINCT s.name,s.description\",\n",
    "    },\n",
    "    {\n",
    "        \"question\": \"what software systems are available\",\n",
    "        \"query\": \"MATCH (s:SoftwareSystem) RETURN DISTINCT s.name,s.description\",\n",
    "    }\n",
    "]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "abeedf5c-8735-4419-9a08-324dd5554dc8",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_core.prompts import FewShotPromptTemplate, PromptTemplate\n",
    "\n",
    "example_prompt = PromptTemplate.from_template(\n",
    "    \"User input: {question}\\nCypher query: {query}\"\n",
    ")\n",
    "\n",
    "promt_prefix = \"\"\"\n",
    "Task: Generate a syntactically correct cypher statement to query a graph database.\n",
    "Instructions:\n",
    "Use only the provided relationship types and properties in the schema.\n",
    "Do not use any other relationship types or properties that are not provided.\n",
    "\n",
    "Schema:\n",
    "{schema}\n",
    "\n",
    "Note: Do not include any explanations or apologies in your responses.\n",
    "Do not respond to any questions that might ask anything else than for you to construct a Cypher statement.\n",
    "Do not include any text except the generated Cypher statement.\n",
    "\n",
    "Here are a few examples of generated Cypher statements for particular questions:\n",
    "\"\"\"\n",
    "\n",
    "promt_suffix=\"\"\"\n",
    "User input: {question}\n",
    "Cypher query:\n",
    "\"\"\"\n",
    "\n",
    "fewShotPrompt = FewShotPromptTemplate(\n",
    "    examples=examples,\n",
    "    example_prompt=example_prompt,\n",
    "    prefix=promt_prefix,\n",
    "    suffix=promt_suffix,\n",
    "    input_variables=[\"question\", \"schema\"],\n",
    ")\n",
    "\n",
    "chain = GraphCypherQAChain.from_llm(graph=graph, llm=llm, cypher_prompt=fewShotPrompt, verbose=True)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "2ae5b263",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "Task: Generate a syntactically correct cypher statement to query a graph database.\n",
      "Instructions:\n",
      "Use only the provided relationship types and properties in the schema.\n",
      "Do not use any other relationship types or properties that are not provided.\n",
      "\n",
      "Schema:\n",
      "{'node_props': {'Person': [{'property': 'tags', 'type': 'STRING'}, {'property': 'name', 'type': 'STRING'}, {'property': 'description', 'type': 'STRING'}], 'SoftwareSystem': [{'property': 'tags', 'type': 'STRING'}, {'property': 'name', 'type': 'STRING'}, {'property': 'description', 'type': 'STRING'}], 'Container': [{'property': 'tags', 'type': 'STRING'}, {'property': 'name', 'type': 'STRING'}, {'property': 'description', 'type': 'STRING'}], 'Component': [{'property': 'tags', 'type': 'STRING'}, {'property': 'name', 'type': 'STRING'}, {'property': 'description', 'type': 'STRING'}], 'Element': [{'property': 'name', 'type': 'STRING'}, {'property': 'embedding', 'type': 'LIST'}, {'property': 'metadata.type', 'type': 'STRING'}, {'property': 'metadata.source', 'type': 'STRING'}, {'property': 'metadata.name', 'type': 'STRING'}, {'property': 'metadata.parent', 'type': 'STRING'}, {'property': 'metadata.tags', 'type': 'STRING'}, {'property': 'text', 'type': 'STRING'}]}, 'rel_props': {'Uses': [{'property': 'description', 'type': 'STRING'}]}, 'relationships': [{'start': 'Person', 'type': 'Uses', 'end': 'Container'}, {'start': 'Person', 'type': 'Uses', 'end': 'SoftwareSystem'}, {'start': 'Person', 'type': 'Uses', 'end': 'Person'}, {'start': 'SoftwareSystem', 'type': 'Uses', 'end': 'Person'}, {'start': 'SoftwareSystem', 'type': 'Uses', 'end': 'SoftwareSystem'}, {'start': 'SoftwareSystem', 'type': 'Contains', 'end': 'Container'}, {'start': 'Container', 'type': 'Uses', 'end': 'Component'}, {'start': 'Container', 'type': 'Uses', 'end': 'Container'}, {'start': 'Container', 'type': 'Uses', 'end': 'SoftwareSystem'}, {'start': 'Container', 'type': 'Contains', 'end': 'Component'}, {'start': 'Component', 'type': 'Uses', 'end': 'Component'}, {'start': 'Component', 'type': 'Uses', 'end': 'Container'}, {'start': 'Component', 'type': 'Uses', 'end': 'SoftwareSystem'}], 'metadata': {'constraint': [], 'index': [{'id': 3, 'name': 'SoftwareSystem_index', 'state': 'ONLINE', 'populationPercent': 100.0, 'type': 'VECTOR', 'entityType': 'NODE', 'labelsOrTypes': ['SoftwareSystem'], 'properties': ['embedding'], 'indexProvider': 'vector-2.0', 'owningConstraint': None, 'lastRead': neo4j.time.DateTime(2024, 5, 2, 13, 32, 27, 222000000, tzinfo=<UTC>), 'readCount': 27, 'trackedSince': neo4j.time.DateTime(2024, 5, 2, 0, 57, 15, 192000000, tzinfo=<UTC>), 'options': {'indexProvider': 'vector-2.0', 'indexConfig': {'vector.dimensions': 1024, 'vector.similarity_function': 'cosine'}}, 'failureMessage': '', 'createStatement': \"CREATE VECTOR INDEX `SoftwareSystem_index` FOR (n:`SoftwareSystem`) ON (n.`embedding`) OPTIONS {indexConfig: {`vector.dimensions`: 1024,`vector.similarity_function`: 'cosine'}, indexProvider: 'vector-2.0'}\"}, {'id': 1, 'name': 'index_343aff4e', 'state': 'ONLINE', 'populationPercent': 100.0, 'type': 'LOOKUP', 'entityType': 'NODE', 'labelsOrTypes': None, 'properties': None, 'indexProvider': 'token-lookup-1.0', 'owningConstraint': None, 'lastRead': neo4j.time.DateTime(2024, 5, 2, 14, 58, 51, 978000000, tzinfo=<UTC>), 'readCount': 967, 'trackedSince': neo4j.time.DateTime(2024, 5, 2, 0, 7, 21, 477000000, tzinfo=<UTC>), 'options': {'indexProvider': 'token-lookup-1.0', 'indexConfig': {}}, 'failureMessage': '', 'createStatement': 'CREATE LOOKUP INDEX `index_343aff4e` FOR (n) ON EACH labels(n)'}, {'id': 2, 'name': 'index_f7700477', 'state': 'ONLINE', 'populationPercent': 100.0, 'type': 'LOOKUP', 'entityType': 'RELATIONSHIP', 'labelsOrTypes': None, 'properties': None, 'indexProvider': 'token-lookup-1.0', 'owningConstraint': None, 'lastRead': neo4j.time.DateTime(2024, 5, 2, 7, 56, 35, 397000000, tzinfo=<UTC>), 'readCount': 4, 'trackedSince': neo4j.time.DateTime(2024, 5, 2, 0, 7, 21, 484000000, tzinfo=<UTC>), 'options': {'indexProvider': 'token-lookup-1.0', 'indexConfig': {}}, 'failureMessage': '', 'createStatement': 'CREATE LOOKUP INDEX `index_f7700477` FOR ()-[r]-() ON EACH type(r)'}]}}\n",
      "\n",
      "Note: Do not include any explanations or apologies in your responses.\n",
      "Do not respond to any questions that might ask anything else than for you to construct a Cypher statement.\n",
      "Do not include any text except the generated Cypher statement.\n",
      "\n",
      "Here are a few examples of generated Cypher statements for particular questions:\n",
      "\n",
      "\n",
      "User input: who are the users\n",
      "Cypher query: MATCH (p:Person) RETURN distinct p\n",
      "\n",
      "User input: what software systems are used by customers\n",
      "Cypher query: MATCH (p:Person)-[:Uses]->(s:SoftwareSystem) WHERE p.tags contains 'Customer' RETURN DISTINCT s.name,s.description\n",
      "\n",
      "User input: what software systems are used by customers to withdraw cash\n",
      "Cypher query: MATCH (p:Person)-[:Uses]->(s:SoftwareSystem) WHERE p.tags contains 'Customer' AND s.description CONTAINS 'withdraw cash' RETURN DISTINCT s.name,s.description\n",
      "\n",
      "User input: what software systems are available\n",
      "Cypher query: MATCH (s:SoftwareSystem) RETURN DISTINCT s.name,s.description\n",
      "\n",
      "\n",
      "User input: What software systems are used by customers to withdraw cash?\n",
      "Cypher query:\n",
      "\n"
     ]
    }
   ],
   "source": [
    "print(fewShotPrompt.format(question=\"What software systems are used by customers to withdraw cash?\", schema=graph.structured_schema))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a8be7b78",
   "metadata": {},
   "source": [
    "### Ask a predefined question\n",
    "\n",
    "Ask a predefined question and the predefined query should be used to generate the answer.\n",
    "\n",
    "```json\n",
    "    {\n",
    "        \"question\": \"what software systems are used by customers\",\n",
    "        \"query\": \"MATCH (p:Person)-[:Uses]->(s:SoftwareSystem) WHERE p.tags contains 'Customer' RETURN DISTINCT s.name,s.description\",\n",
    "    }\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "id": "f9926c14",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001b[1m> Entering new GraphCypherQAChain chain...\u001b[0m\n",
      "Generated Cypher:\n",
      "\u001b[32;1m\u001b[1;3mMATCH (p:Person)-[:Uses]->(s:SoftwareSystem) WHERE p.tags contains 'Customer'\n",
      "RETURN DISTINCT s.name, s.description\u001b[0m\n",
      "Full Context:\n",
      "\u001b[32;1m\u001b[1;3m[{'s.name': 'ATM', 's.description': 'Allows customers to withdraw cash.'}, {'s.name': 'Internet Banking System', 's.description': 'Allows customers to view information about their bank accounts, and make payments.'}]\u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'query': 'What software systems are used by customers?',\n",
       " 'result': 'Customers use ATM software for withdrawing cash and Internet Banking System software for viewing information about their bank accounts and making payments.'}"
      ]
     },
     "execution_count": 22,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "response = chain.invoke(\"What software systems are used by customers?\")\n",
    "response"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "090f50ef",
   "metadata": {},
   "source": [
    "### Ask a question that is not in the predefined list\n",
    "\n",
    "The QA Chain should be able to find the similar question in the predefined list and use the corresponding query to generate the answer."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "id": "28b81b5f",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001b[1m> Entering new GraphCypherQAChain chain...\u001b[0m\n",
      "Generated Cypher:\n",
      "\u001b[32;1m\u001b[1;3mMATCH (p:Person)-[:Uses]->(s:SoftwareSystem) WHERE p.tags contains 'Bank Staff' RETURN DISTINCT s.name,s.description\u001b[0m\n",
      "Full Context:\n",
      "\u001b[32;1m\u001b[1;3m[{'s.name': 'Mainframe Banking System', 's.description': 'Stores all of the core banking information about customers, accounts, transactions, etc.'}]\u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'query': 'What software systems are used by bank staff?',\n",
       " 'result': 'The Mainframe Banking System is used by bank staff to store all of the core banking information about customers, accounts, and transactions.'}"
      ]
     },
     "execution_count": 23,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "response = chain.invoke(\"What software systems are used by bank staff?\")\n",
    "response"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "id": "f2da7faf",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001b[1m> Entering new GraphCypherQAChain chain...\u001b[0m\n",
      "Generated Cypher:\n",
      "\u001b[32;1m\u001b[1;3mMATCH (p:Person)-[:Uses]->(s:SoftwareSystem)\n",
      " WHERE p.tags contains 'Customer' AND s.description CONTAINS 'withdraw cash'\n",
      " RETURN DISTINCT s.name, s.description\u001b[0m\n",
      "Full Context:\n",
      "\u001b[32;1m\u001b[1;3m[{'s.name': 'ATM', 's.description': 'Allows customers to withdraw cash.'}]\u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'query': 'what software systems can be used to withdraw cash?',\n",
       " 'result': 'An ATM (Automated Teller Machine) software system can be used to withdraw cash.'}"
      ]
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "response = chain.invoke(\"what software systems can be used to withdraw cash?\")\n",
    "response"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4500a922",
   "metadata": {},
   "source": [
    "### Semantic Search\n",
    "\n",
    "The cypher statements generated for the above examples are using simple matching against keywords, e.g. `p.tags contains 'Bank Staff'` \n",
    "Question :\n",
    "`What software systems are used by bank staff?`\n",
    "\n",
    "Generated Cypher:\n",
    "`MATCH (p:Person)-[:Uses]->(s:SoftwareSystem) WHERE p.tags contains 'Bank Staff' RETURN DISTINCT s.name,s.description`\n",
    "\n",
    "Result: \n",
    "`The Mainframe Banking System is used by bank staff to store all of the core banking information about customers, accounts, and transactions.`\n",
    "\n",
    "if we ask the question in another way, QA Chain will not be able to answer correctly, it should use semnatic search instead to resolve the problem. \n",
    "\n",
    "https://python.langchain.com/docs/use_cases/graph/semantic/\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "id": "cd427404",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001b[1m> Entering new GraphCypherQAChain chain...\u001b[0m\n",
      "Generated Cypher:\n",
      "\u001b[32;1m\u001b[1;3mMATCH (p:Person)-[:Uses]->(s:SoftwareSystem)\n",
      " WHERE p.tags contains 'Customer'\n",
      " AND s.description CONTAINS 'store customer information'\n",
      " RETURN DISTINCT s.name, s.description\u001b[0m\n",
      "Full Context:\n",
      "\u001b[32;1m\u001b[1;3m[]\u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'query': 'what software systems can be used to store customer information?',\n",
       " 'result': 'There are several software systems that can be used to store customer information. Some common options include Customer Relationship Management (CRM) systems such as Salesforce, Microsoft Dynamics, or HubSpot. Other options include database management systems like MySQL or Oracle, or cloud storage services like Google Drive or Amazon S3. The specific system used may depend on the size and needs of the business.'}"
      ]
     },
     "execution_count": 25,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "response = chain.invoke(\"what software systems can be used to store customer information?\")\n",
    "response"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "id": "d5eeb77a",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001b[1m> Entering new GraphCypherQAChain chain...\u001b[0m\n",
      "Generated Cypher:\n",
      "\u001b[32;1m\u001b[1;3mMATCH (p:Person)-[:Uses]->(s:SoftwareSystem) WHERE p.tags contains 'Customer' AND s.description CONTAINS 'banking information' RETURN DISTINCT s.name,s.description\u001b[0m\n",
      "Full Context:\n",
      "\u001b[32;1m\u001b[1;3m[]\u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'query': 'what software systems can be used by customer to view their banking information?',\n",
       " 'result': 'The provided information does not specify which software systems can be used by customers to view their banking information. Therefore, I cannot provide a definitive answer.'}"
      ]
     },
     "execution_count": 26,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "response = chain.invoke(\"what software systems can be used by customer to view their banking information?\")\n",
    "response"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0797c404",
   "metadata": {},
   "source": [
    "#### Create Neo4j Vector Store from an existing graph database\n",
    "\n",
    "Create embedding vector for the SoftwareSystem node from existing neo4j graph database. A new **embedding** property will be added to the SoftwareSystem node.\n",
    "\n",
    "- [MistralAIEmbeddings](https://python.langchain.com/docs/integrations/text_embedding/mistralai/)\n",
    "\n",
    "- [Neo4j Vector](https://python.langchain.com/docs/integrations/vectorstores/neo4jvector/)\n",
    "\n",
    "- [Create Neo4jVector from an existing graph](https://api.python.langchain.com/en/latest/_modules/langchain_community/vectorstores/neo4j_vector.html#Neo4jVector.from_existing_graph)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "id": "1d0d2980",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages/langchain_mistralai/embeddings.py:105: UserWarning: Could not download mistral tokenizer from Huggingface for calculating batch sizes. Set a Huggingface token via the HF_TOKEN environment variable to download the real tokenizer. Falling back to a dummy tokenizer that uses `len()`.\n",
      "  warnings.warn(\n"
     ]
    }
   ],
   "source": [
    "from langchain_community.vectorstores import Neo4jVector\n",
    "from langchain_mistralai import MistralAIEmbeddings\n",
    "\n",
    "# # Initialize and return a Neo4jVector instance from an existing graph.\n",
    "vectorestore = Neo4jVector.from_existing_graph(\n",
    "    embedding=MistralAIEmbeddings(mistral_api_key=os.getenv(\"MISTRAL_API_KEY\")),\n",
    "    node_label=\"SoftwareSystem\",\n",
    "    embedding_node_property=\"embedding\",\n",
    "    text_node_properties=[\"description\"],\n",
    "    index_name=\"SoftwareSystem_index\",\n",
    "    url=os.getenv(\"NEO4J_URI\"), username=os.getenv(\"NEO4J_USERNAME\"), password=os.getenv(\"NEO4J_PASSWORD\")\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "id": "86082cdc",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[(Document(page_content='\\ndescription: Stores all of the core banking information about customers, accounts, transactions, etc.', metadata={'tags': 'Element,Software System,Existing System', 'name': 'Mainframe Banking System'}),\n",
       "  0.8742513656616211)]"
      ]
     },
     "execution_count": 28,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "result = vectorestore.similarity_search_with_score(\"what software systems can be used to store customer information?\", k=1)\n",
    "result"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "id": "06bb2b8f",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[(Document(page_content='\\ndescription: Allows customers to view information about their bank accounts, and make payments.', metadata={'tags': 'Element,Software System', 'name': 'Internet Banking System'}),\n",
       "  0.9012417197227478)]"
      ]
     },
     "execution_count": 29,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "result = vectorestore.similarity_search_with_score(\"what software systems can be used by customer to view their banking information?\", k=1)\n",
    "result"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fa37226b",
   "metadata": {},
   "source": [
    "#### Create Neo4j Vector Store from an existing embedding index\n",
    "\n",
    "- [MistralAIEmbeddings](https://python.langchain.com/docs/integrations/text_embedding/mistralai/)\n",
    "\n",
    "- [Neo4j Vector](https://python.langchain.com/docs/integrations/vectorstores/neo4jvector/)\n",
    "\n",
    "- [Create Neo4jVector from an existing index](https://api.python.langchain.com/en/latest/_modules/langchain_community/vectorstores/neo4j_vector.html#Neo4jVector.from_existing_index)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "127759c4",
   "metadata": {},
   "outputs": [],
   "source": [
    "# # First we create sample data and index in graph\n",
    "# vectorestore.query(\n",
    "#     \"MERGE (p1)-[:Uses {description:'example text', embedding:$embedding}]->(p2)\",\n",
    "#     params={\"embedding\": MistralAIEmbeddings(mistral_api_key=os.getenv(\"MISTRAL_API_KEY\")).embed_query(\"example text\")},\n",
    "# )\n",
    "# # Create a vector index\n",
    "# relationship_index = \"relationship_vector\"\n",
    "# vectorestore.query(\n",
    "#     \"\"\"\n",
    "# CREATE VECTOR INDEX $relationship_index\n",
    "# IF NOT EXISTS\n",
    "# FOR ()-[r:Uses]-() ON (r.embedding)\n",
    "# OPTIONS {indexConfig: {\n",
    "#  `vector.dimensions`: 1536,\n",
    "#  `vector.similarity_function`: 'cosine'\n",
    "# }}\n",
    "# \"\"\",\n",
    "#     params={\"relationship_index\": relationship_index},\n",
    "# )\n",
    "\n",
    "# relationship_vector = Neo4jVector.from_existing_relationship_index(\n",
    "#     MistralAIEmbeddings(mistral_api_key=os.getenv(\"MISTRAL_API_KEY\")),\n",
    "#     url=os.getenv(\"NEO4J_URI\"), username=os.getenv(\"NEO4J_USERNAME\"), password=os.getenv(\"NEO4J_PASSWORD\"),\n",
    "#     index_name=relationship_index,\n",
    "#     text_node_property=\"description\",\n",
    "# )\n",
    "# relationship_vector.similarity_search(\"Example\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d80afb9d",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "42508a7b",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages/langchain_mistralai/embeddings.py:105: UserWarning: Could not download mistral tokenizer from Huggingface for calculating batch sizes. Set a Huggingface token via the HF_TOKEN environment variable to download the real tokenizer. Falling back to a dummy tokenizer that uses `len()`.\n",
      "  warnings.warn(\n"
     ]
    }
   ],
   "source": [
    "# Create Neo4jVector instance from an existing index.\n",
    "from langchain_community.vectorstores import Neo4jVector\n",
    "from langchain_mistralai import MistralAIEmbeddings\n",
    "\n",
    "embedding=MistralAIEmbeddings(mistral_api_key=os.getenv(\"MISTRAL_API_KEY\"))\n",
    "\n",
    "retrieval_query = \"\"\"\n",
    "RETURN node.text AS text, score, {tags: node.`metadata.tags`, name: node.`metadata.name`} AS metadata\n",
    "\"\"\"\n",
    "# vectorestore2 = Neo4jVector.from_existing_index(\n",
    "#     embedding=embedding,\n",
    "#     url=os.getenv(\"NEO4J_URI\"), username=os.getenv(\"NEO4J_USERNAME\"), password=os.getenv(\"NEO4J_PASSWORD\"),\n",
    "#     keyword_index_name=\"Element_unique_idx\",\n",
    "#     index_name=\"Element_index\",\n",
    "#     retrieval_query=retrieval_query,\n",
    "#     embedding_node_property=\"embedding\",\n",
    "#     text_node_property=\"text\",\n",
    "#     node_label=\"Element\",\n",
    "# )\n",
    "\n",
    "vectorestore2 = Neo4jVector(\n",
    "    embedding=embedding,\n",
    "    url=os.getenv(\"NEO4J_URI\"), username=os.getenv(\"NEO4J_USERNAME\"), password=os.getenv(\"NEO4J_PASSWORD\"),\n",
    "    keyword_index_name=\"Element_unique_idx\",\n",
    "    index_name=\"Element_index\",\n",
    "    retrieval_query=retrieval_query,\n",
    "    embedding_node_property=\"embedding\",\n",
    "    text_node_property=\"text\",\n",
    "    node_label=\"Element\",\n",
    ")        "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "08add5c6",
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "embedding_dimension, index_type = vectorestore2.retrieve_existing_index()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "4ab99762",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "None\n"
     ]
    }
   ],
   "source": [
    "print(index_type)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "0a943c09",
   "metadata": {},
   "outputs": [],
   "source": [
    "index_query=\"\"\"\n",
    "SHOW INDEXES YIELD name, type, entityType, labelsOrTypes,properties, options \n",
    "WHERE type = 'VECTOR' AND (name = $index_name OR (labelsOrTypes[0] = $node_label AND properties[0] = $embedding_node_property)) \n",
    "RETURN name, type, entityType, labelsOrTypes,properties, options\n",
    "\"\"\"\n",
    "\n",
    "# index_query=\"\"\"\n",
    "# SHOW INDEXES YIELD name, type, entityType, labelsOrTypes,properties, options \n",
    "# RETURN name, type, entityType, labelsOrTypes,properties, options\n",
    "# \"\"\"\n",
    "\n",
    "index_information = graph.query(\n",
    "            index_query,\n",
    "            params={\n",
    "                \"index_name\": \"Element_index\",\n",
    "                \"node_label\": \"Element\",\n",
    "                \"embedding_node_property\": \"text\",\n",
    "            },\n",
    "        )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "16cf7f1b",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[]\n"
     ]
    }
   ],
   "source": [
    "print(index_information)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "98de365e",
   "metadata": {},
   "outputs": [],
   "source": [
    "result = vectorestore2.similarity_search(\"what software systems can be used to store customer information?\", k=1)\n",
    "result"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9c4d9f9e",
   "metadata": {},
   "outputs": [],
   "source": [
    "retriever = vectorestore.as_retriever()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "15f37d24",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain.chains import RetrievalQAWithSourcesChain\n",
    "chain = RetrievalQAWithSourcesChain.from_chain_type(\n",
    "    llm, chain_type=\"stuff\", retriever=retriever\n",
    ")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4f2e019e",
   "metadata": {},
   "outputs": [],
   "source": [
    "chain.invoke(\n",
    "    {\"question\": \"what software systems can be used to store customer information?\"},\n",
    "    return_only_outputs=True,\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d6102ba1",
   "metadata": {},
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
